package com.example.demo.service;


import com.example.demo.pojo.entity.ConflictRate;
import com.example.demo.pojo.entity.Person;
import com.example.demo.utils.MyPinyinUtils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * 自定义散列表主要业务代码类
 * <p>
 * 利用散列表设计实现电话号码查找系统
 * 1.每个记录包括用户名，电话，地址
 * 2.界面输入，以用户名拼音为关键字建立散列表
 * 3.采用某个方法解决冲突
 * 4.查找并显示给定用户的电话号码的信息
 * 5.至少设计2种散列函数，比较冲突率
 *
 * @author Wei
 * @date 2024/05/19
 */
public class MyHash {

    private LinkedList<Person>[] table;
    private int size;

    /**
     * 初始化哈希表
     *
     * @param size 大小
     */
    public MyHash(int size) {
        this.size = size;
        table = new LinkedList[size];
        for (int i = 0; i < size; i++) {
            table[i] = new LinkedList<>();
        }

        /**
         * 初始哈希表
         * 提前插入数据
         */
        insert("曹操", "12345678901", "许昌曹操府");
        insert("刘备", "12345678902", "成都刘备巷");
        insert("孙权", "12345678903", "江东孙家湾");
        insert("关羽", "12345678904", "荆州关公桥");
        insert("张飞", "12345678905", "涿郡张翼德街");
        insert("赵云", "12345678906", "常山赵子龙道");
        insert("黄忠", "12345678907", "长沙黄老将军路");
        insert("马超", "12345678908", "西凉马孟起广场");
        insert("魏延", "12345678909", "汉中魏延巷");
        insert("姜维", "12345678910", "天水姜维街");
        insert("庞统", "12345678911", "荆州凤雏路");
        insert("法正", "12345678912", "成都法正大道");
        insert("徐庶", "12345678913", "颖川徐元直街");
        insert("贾诩", "12345678914", "长安贾文和路");
        insert("郭嘉", "12345678915", "颍川郭奉孝巷");
        insert("程昱", "12345678916", "东阿程昱桥");
        insert("荀攸", "12345678917", "荀攸故居");
        insert("典韦", "12345678918", "陈留典韦路");
        insert("夏侯惇", "12345678919", "沛国夏侯惇街");
        insert("乐进", "12345678920", "阳翟乐进巷");
        insert("于禁", "12345678921", "泰山于禁路");
        insert("张辽", "12345678922", "雁门张文远桥");
        insert("许褚", "12345678923", "谯县许褚街");
        insert("李典", "12345678924", "李典故里");
        insert("张郃", "12345678925", "河间张郃大道");
        insert("徐晃", "12345678926", "上党徐晃路");
        insert("文聘", "12345678927", "江夏文聘巷");
        insert("曹洪", "12345678928", "沛国曹洪桥");
        insert("曹仁", "12345678929", "谯郡曹仁街");
        insert("曹真", "12345678930", "真定曹真路");
        insert("夏侯渊", "12345678931", "沛国夏侯渊巷");
        insert("曹休", "12345678932", "谯郡曹休路");
        insert("张春华", "12345678933", "华容张春华街");
        insert("甄姬", "12345678934", "中山甄姬巷");
        insert("蔡文姬", "12345678935", "陈留蔡文姬路");
        insert("貂蝉", "12345678936", "司州貂蝉桥");
        insert("大乔", "12345678937", "江东大乔街");
        insert("小乔", "12345678938", "江东小乔路");
        insert("黄月英", "12345678939", "荆州黄月英巷");
        insert("祝融", "12345678940", "南蛮祝融路");
        insert("马云騄", "12345678941", "西凉马云騄桥");
        insert("孙尚香", "12345678942", "吴郡孙尚香街");
        insert("步练师", "12345678943", "吴郡步练师路");
        insert("甘宁", "12345678944", "江夏甘宁巷");
        insert("吕蒙", "12345678945", "吴郡吕蒙路");
        insert("陆逊", "12345678946", "吴郡陆逊桥");
        insert("周泰", "12345678947", "吴郡周泰街");
        insert("丁奉", "12345678948", "庐江丁奉路");
        insert("太史慈", "12345678949", "东莱太史慈巷");
        insert("凌统", "12345678950", "会稽凌统路");
    }

    /**
     * 新增一个联系人
     *
     * @param name    名称
     * @param phone   电话
     * @param address 住址
     *                返回0表示插入
     *                返回1表示修改
     */
    public Integer insert(String name, String phone, String address) {
        int index = hash(name);
        Person entry = Person.builder().name(name).address(address).phone(phone).build();
        LinkedList<Person> list = table[index];

        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).getName().equals(entry.getName())) {
                //    已经存在则为修改操作
                entry.setId(list.get(i).getId());
                list.set(i, entry);
                return 1;
            }
        }
        entry.setId(Person.getCurId());
        list.add(entry);
        return 0;
    }

    /**
     * 查找指定姓名的联系人
     *
     * @param name 名称
     * @return {@link Person}
     */
    public Person search(String name) {
        int index = hash(name);
        System.out.println("index:" + index);
        LinkedList<Person> list = table[index];
        for (Person entry : list) {
            if (entry.getName().equals(name)) {
                return entry;
            }
        }
        return null;
    }

    /**
     * 散列算法
     *
     * @param name 名称
     * @return int
     */
    private int hash(String name) {
        String pinyin = MyPinyinUtils.toPinyin(name);
        // 哈希实现1 拼音长度对哈希表长度取余
        //int hash1 = pinyin.length() % size;
        //System.out.println("name:" + name + ";拼音:" + pinyin + ";index:" + hash1);
        //
        //return hash1;


        //    哈希实现2 对拼音进行md5运算然后将每一位相加再进行取余
        //MessageDigest md = null;
        //try {
        //    md = MessageDigest.getInstance("md5");
        //} catch (NoSuchAlgorithmException e) {
        //    throw new RuntimeException(e);
        //}
        //byte[] digestBytes = md.digest(pinyin.getBytes());
        //int hash2 = 0;
        //for (byte b : digestBytes) {
        //    // 取字节的低8位 0xff => 1111 1111
        //    hash2 += b & 0xFF;
        //}
        //hash2 %= size; // 对哈希表大小取余
        //System.out.println("name:" + name + ";拼音:" + pinyin + ";index:" + hash2);
        //return hash2;

        // 哈希实现3
        int h = name.hashCode();

        int hash3 = h ^ (h >>> 16);
        hash3 %= size; // 对哈希表大小取余
        System.out.println("name:" + name + ";拼音:" + pinyin + ";index:" + hash3);
        return hash3;
    }


    /**
     * 删除联系人
     *
     * @param name 名称
     */
    public Integer delete(String name) {
        int index = hash(name);
        LinkedList<Person> list = table[index];
        Person toDelete = null;
        for (Person entry : list) {
            // 不能便利的过程中删除
            if (entry.getName().equals(name)) {
                toDelete = entry;
                break;
            }
        }
        if (toDelete != null) {
            list.remove(toDelete);
            return 1;
        }
        return 0;
    }

    /**
     * 获取所有联系人
     *
     * @return {@link List}<{@link Person}>
     */
    public List<Person> getAllContacts() {
        List<Person> allContacts = new ArrayList<>();
        for (LinkedList<Person> list : table) {
            allContacts.addAll(list);
        }
        return allContacts;
    }


    /**
     * 获取冲突率
     *
     * @return {@link ConflictRate }
     */
    public ConflictRate getConflictRate() {

        int count = Math.toIntExact(Person.getCurId());
        int conflictCount = 0;
        for (LinkedList<Person> item : table) {
            if (item.size() > 1)
                conflictCount += item.size();
        }

        ConflictRate res = new ConflictRate(size, count, conflictCount);

        System.out.println("---------------------------------------------");
        System.out.println("哈希表大小:" + res.getLength());
        System.out.println("记录总数:" + res.getAllRecords());
        System.out.println("发生冲突数:" + res.getConflictCount());
        System.out.println("冲突率:" + String.format("%.3f", (double) res.getConflictCount() / res.getAllRecords()));
        System.out.println("---------------------------------------------");

        return res;
    }

}
